Skip to content

Use for instead of next in contextlib.contextmanager#141275

Open
brandtbucher wants to merge 2 commits into
python:mainfrom
brandtbucher:contextlib
Open

Use for instead of next in contextlib.contextmanager#141275
brandtbucher wants to merge 2 commits into
python:mainfrom
brandtbucher:contextlib

Conversation

@brandtbucher

Copy link
Copy Markdown
Member

This is a fun little optimization. According to some local microbenchmarks (basically entering and exiting context managers a million times), turning these next calls into for loops saves about 25% of the overhead this class introduces (~5% due to __enter__ and ~20% due to __exit__). This is because:

  • The wrapped iterator is usually a generator. We specialize for loops for generators, so instead of calling through the C code for next and re-entering the interpreter, we can "inline" the frame push instead. (This is also more JIT-friendly.)
  • We don't care about the actual StopIteration exceptions here, just whether or not they were raised. Raising exceptions is expensive, and the interpreter has an optimization to avoid actually raising StopIteration in normal for loops. (This is especially helpful in __exit__, where we advance the generator, expect a StopIteration to be raised, and then just throw it away!)

This change isn't worth it for the async variant of this function, since neither of the above optimizations apply to async for loops.

@brandtbucher brandtbucher self-assigned this Nov 9, 2025
@brandtbucher brandtbucher added performance Performance or resource usage skip issue skip news stdlib Standard Library Python modules in the Lib/ directory labels Nov 9, 2025
@picnixz

picnixz commented Nov 9, 2025

Copy link
Copy Markdown
Member

For tracking purposes, could you create an issue for that please? I also think it's worth a NEWS entry for people who are interested in this kind of optimization and understanding it.

@picnixz picnixz removed the skip issue label Nov 9, 2025
@serhiy-storchaka

Copy link
Copy Markdown
Member

The wrapped iterator is usually a generator.

What if it is not? for calls __iter__, not only __next__.

@brandtbucher

brandtbucher commented Nov 11, 2025

Copy link
Copy Markdown
Member Author

Sure, I can create an issue/NEWS.

What if it is not? for calls __iter__, not only __next__.

The docs for contextlib.contextmanager say that this function only works with functions that return generator-iterators (which define __iter__ the normal way):

The function being decorated must return a generator-iterator when called.

Besides, this still works (just as it does today) if another type of iterator happens to be returned, since they're required by the iteration protocol to define an __iter__ returning self anyways:

Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted.

@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions Bot added the stale Stale PR or inactive for long period of time. label May 2, 2026
@markshannon

Copy link
Copy Markdown
Member

@brandtbucher want to create an issue, so we can merge this?
I think we can skip the news item as there is no change in behavior

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting core review performance Performance or resource usage stale Stale PR or inactive for long period of time. stdlib Standard Library Python modules in the Lib/ directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants